package com.design.fitment.view;

/**
 * Created by 娇哥 on 2016/1/12.
 */

    import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;

import java.util.LinkedList;
import java.util.Queue;

    public class HorizontalListView extends AdapterView<ListAdapter> {

        public boolean mAlwaysOverrideTouch = true;
        protected ListAdapter mAdapter;
        private int mLeftViewIndex = -1;
        private int mRightViewIndex = 0;
        protected int mCurrentX;
        protected int mNextX;
        private int mMaxX = Integer.MAX_VALUE;
        private int mDisplayOffset = 0;
        protected Scroller mScroller;
        private GestureDetector mGesture;
        private Queue<View> mRemovedViewQueue = new LinkedList<View>();
        private OnItemSelectedListener mOnItemSelected;
        private OnItemClickListener mOnItemClicked;
        private OnItemLongClickListener mOnItemLongClicked;
        private boolean mDataChanged = false;


        public HorizontalListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView();
        }

        private synchronized void initView() {
            mLeftViewIndex = -1;
            mRightViewIndex = 0;
            mDisplayOffset = 0;
            mCurrentX = 0;
            mNextX = 0;
            mMaxX = Integer.MAX_VALUE;
            mScroller = new Scroller(getContext());
            mGesture = new GestureDetector(getContext(), mOnGesture);
        }

        @Override
        public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
            mOnItemSelected = listener;
        }

        @Override
        public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
            mOnItemClicked = listener;
        }

        @Override
        public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
            mOnItemLongClicked = listener;
        }

        private DataSetObserver mDataObserver = new DataSetObserver() {

            @Override
            public void onChanged() {
                synchronized(HorizontalListView.this){
                    mDataChanged = true;
                }
                invalidate();
                requestLayout();
            }

            @Override
            public void onInvalidated() {
                reset();
                invalidate();
                requestLayout();
            }

        };

        @Override
        public ListAdapter getAdapter() {
            return mAdapter;
        }

        @Override
        public View getSelectedView() {
            //TODO: implement
            return null;
        }

        @Override
        public void setAdapter(ListAdapter adapter) {
            if(mAdapter != null) {
                mAdapter.unregisterDataSetObserver(mDataObserver);
            }
            mAdapter = adapter;
            mAdapter.registerDataSetObserver(mDataObserver);
            reset();
        }

        private synchronized void reset(){
            initView();
            removeAllViewsInLayout();
            requestLayout();
        }

        @Override
        public void setSelection(int position) {
            //TODO: implement
        }

        private void addAndMeasureChild(final View child, int viewPos) {
            LayoutParams params = child.getLayoutParams();
            if(params == null) {
                params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
            }

            addViewInLayout(child, viewPos, params, true);
            child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                    MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
        }



        @Override
        protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);

            if(mAdapter == null){
                return;
            }

            if(mDataChanged){
                int oldCurrentX = mCurrentX;
                initView();
                removeAllViewsInLayout();
                mNextX = oldCurrentX;
                mDataChanged = false;
            }

            if(mScroller.computeScrollOffset()){
                int scrollx = mScroller.getCurrX();
                mNextX = scrollx;
            }

            if(mNextX <= 0){
                mNextX = 0;
                mScroller.forceFinished(true);
            }
            if(mNextX >= mMaxX) {
                mNextX = mMaxX;
                mScroller.forceFinished(true);
            }

            int dx = mCurrentX - mNextX;

            removeNonVisibleItems(dx);
            fillList(dx);
            positionItems(dx);

            mCurrentX = mNextX;

            if(!mScroller.isFinished()){
                post(new Runnable(){
                    @Override
                    public void run() {
                        requestLayout();
                    }
                });

            }
        }

        private void fillList(final int dx) {
            int edge = 0;
            View child = getChildAt(getChildCount()-1);
            if(child != null) {
                edge = child.getRight();
            }
            fillListRight(edge, dx);

            edge = 0;
            child = getChildAt(0);
            if(child != null) {
                edge = child.getLeft();
            }
            fillListLeft(edge, dx);


        }

        private void fillListRight(int rightEdge, final int dx) {
            while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {

                View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
                addAndMeasureChild(child, -1);
                rightEdge += child.getMeasuredWidth();

                if(mRightViewIndex == mAdapter.getCount()-1) {
                    mMaxX = mCurrentX + rightEdge - getWidth();
                }

                if (mMaxX < 0) {
                    mMaxX = 0;
                }
                mRightViewIndex++;
            }

        }

        private void fillListLeft(int leftEdge, final int dx) {
            while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
                View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
                addAndMeasureChild(child, 0);
                leftEdge -= child.getMeasuredWidth();
                mLeftViewIndex--;
                mDisplayOffset -= child.getMeasuredWidth();
            }
        }

        private void removeNonVisibleItems(final int dx) {
            View child = getChildAt(0);
            while(child != null && child.getRight() + dx <= 0) {
                mDisplayOffset += child.getMeasuredWidth();
                mRemovedViewQueue.offer(child);
                removeViewInLayout(child);
                mLeftViewIndex++;
                child = getChildAt(0);

            }

            child = getChildAt(getChildCount()-1);
            while(child != null && child.getLeft() + dx >= getWidth()) {
                mRemovedViewQueue.offer(child);
                removeViewInLayout(child);
                mRightViewIndex--;
                child = getChildAt(getChildCount()-1);
            }
        }

        private void positionItems(final int dx) {
            if(getChildCount() > 0){
                mDisplayOffset += dx;
                int left = mDisplayOffset;
                for(int i=0;i<getChildCount();i++){
                    View child = getChildAt(i);
                    int childWidth = child.getMeasuredWidth();
                    child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
                    left += childWidth + child.getPaddingRight();
                }
            }
        }

        public synchronized void scrollTo(int x) {
            mScroller.startScroll(mNextX, 0, x - mNextX, 0);
            requestLayout();
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            boolean handled = super.dispatchTouchEvent(ev);
            handled |= mGesture.onTouchEvent(ev);
            return handled;
        }

        protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                                  float velocityY) {
            synchronized(HorizontalListView.this){
                mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
            }
            requestLayout();

            return true;
        }

        protected boolean onDown(MotionEvent e) {
            mScroller.forceFinished(true);
            return true;
        }

        private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

            @Override
            public boolean onDown(MotionEvent e) {
                return HorizontalListView.this.onDown(e);
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                                   float velocityY) {
                return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2,
                                    float distanceX, float distanceY) {

                synchronized(HorizontalListView.this){
                    mNextX += (int)distanceX;
                }
                requestLayout();

                return true;
            }

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                for(int i=0;i<getChildCount();i++){
                    View child = getChildAt(i);
                    if (isEventWithinView(e, child)) {
                        if(mOnItemClicked != null){
                            mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
                        }
                        if(mOnItemSelected != null){
                            mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
                        }
                        break;
                    }

                }
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                int childCount = getChildCount();
                for (int i = 0; i < childCount; i++) {
                    View child = getChildAt(i);
                    if (isEventWithinView(e, child)) {
                        if (mOnItemLongClicked != null) {
                            mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
                        }
                        break;
                    }

                }
            }

            private boolean isEventWithinView(MotionEvent e, View child) {
                Rect viewRect = new Rect();
                int[] childPosition = new int[2];
                child.getLocationOnScreen(childPosition);
                int left = childPosition[0];
                int right = left + child.getWidth();
                int top = childPosition[1];
                int bottom = top + child.getHeight();
                viewRect.set(left, top, right, bottom);
                return viewRect.contains((int) e.getRawX(), (int) e.getRawY());
            }
        };



    }
